﻿// 
// ProtocolManager.cs
//  
// Author:
//       Vladimir Aubrecht <tomas.vondracek@gmail.com>
// 
// Copyright (c) 2010 Tomas Vondracek
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Reflection;
using IM.Interfaces.Protocols;
using IM.Interfaces;
using IM.DataTypes;

namespace IM.Protocols
{
    public class ProtocolManager : IProtocolManager
    {
        public event ObjectHandler OnLogin;
        public event ErrorHandler OnError;
        public event MessageHandler OnMessage;
        public event ObjectHandler OnClose;
        public event ContactListHandler OnContactListLoad;
        public event ConnectionStateHandler OnConnectionStateChanged;

        Dictionary<String, IProtocolAdapter> protocols = new Dictionary<string, IProtocolAdapter>();

        public IProtocolAdapter this [string name]
        {
            get { return protocols[name]; }
        }

        public IProtocolAdapter this[int index]
        {
            get { return protocols.Values.ElementAt<IProtocolAdapter>(index); }
        }

        public int Count
        {
            get { return this.protocols.Count; }
        }

        public ProtocolManager()
        {
            
        }

        public bool Contains(String protocolName)
        {
            return this.protocols.ContainsKey(protocolName);
        }

        public void Close()
        {
            foreach (var protocol in protocols)
            {
                protocol.Value.Protocol.Logout();

                if (OnClose != null)
                    OnClose(protocol);
            }
        }

        public bool CreateProtocol(IProtocolAdapter adapter)
        {
            if (!adapter.IsProtocolCreated)
                return false;

            this.protocols.Add(adapter.Protocol.AccountName, adapter);
            RegisterProtocolEvents(adapter.Protocol);

            return adapter.IsProtocolCreated;
        }

        public bool CreateProtocol(String name, String path)
        {
            ProtocolAdapter adapter = new ProtocolAdapter(name, path);

            if (!adapter.IsProtocolCreated)
                return false;

            name = GetUniqueProtocolName(name);

            this.protocols.Add(name, adapter);
            RegisterProtocolEvents(adapter.Protocol);

            return adapter.IsProtocolCreated;
        }

        private String GetUniqueProtocolName(String name)
        {
            int i = 0;
            while (this.protocols.ContainsKey(String.Format("{0}{1}", name, i))) i++;

            if (i > 0 || this.protocols.ContainsKey(name))
                name = String.Format("{0}{1}", name, i);
            return name;
        }

        public void DestroyProtocol(String name)
        {
            IProtocolAdapter adapter = this[name];
            adapter.DestroyAdapter();
            protocols.Remove(name);
        }

        private void RegisterProtocolEvents(IProtocol protocol)
        {
            protocol.OnLogin += new ObjectHandler(protocol_OnLogin);
            protocol.OnError += new ErrorHandler(protocol_OnError);
            protocol.OnMessage += new MessageHandler(protocol_OnMessage);
            protocol.OnClose += new ObjectHandler(protocol_OnClose);
            protocol.OnContactListLoad += new ContactListHandler(protocol_OnContactListLoad);
            protocol.OnConnectionStateChanged += new ConnectionStateHandler(protocol_OnConnectionStateChanged);
        }

        public IProtocolAdapter[] GetAvaibleProtocols(String path)
        {
            List<IProtocolAdapter> avaibleProtocols = new List<IProtocolAdapter>();
            String currentPath = Directory.GetCurrentDirectory();
            Directory.SetCurrentDirectory(path);
            AppDomain.CurrentDomain.AppendPrivatePath(Directory.GetCurrentDirectory());

            var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll");

            foreach (var filename in files)
            {

                String name = GetUniqueProtocolName(Path.GetFileNameWithoutExtension(filename).ToLower());

                ProtocolAdapter adapter = new ProtocolAdapter(name, filename);

                if (!adapter.IsProtocolCreated)
                    continue;

                avaibleProtocols.Add(adapter);
            }

            Directory.SetCurrentDirectory(currentPath);

            return avaibleProtocols.ToArray();
        }

        void protocol_OnConnectionStateChanged(object sender, ConnectionStates state)
        {
            Logging.Logger.AddInfo(sender.ToString() + ": connection state changed to " + state.ToString());
            if (OnConnectionStateChanged != null)
                OnConnectionStateChanged(sender, state);
        }

        void protocol_OnContactListLoad(object sender, User[] users)
        {
            if (sender != null && users != null)
                Logging.Logger.AddInfo(string.Format("{0}: OnContactListLoad with {1} users", sender.ToString(), users.Count()));

            if (OnContactListLoad != null)
                OnContactListLoad(sender, users);
        }

        void protocol_OnClose(object sender)
        {
            if (OnClose != null)
                OnClose(sender);
        }

        void protocol_OnMessage(object sender, User from, string body)
        {
            if (OnMessage != null)
                OnMessage(sender, from, body);
        }

        void protocol_OnError(object sender, Exception ex)
        {
            if (OnError != null)
                OnError(sender, ex);
        }

        void protocol_OnLogin(object sender)
        {
            if (sender is IProtocol)
            {
                var protocol = (sender as IProtocol);
                if (protocol != null && protocol.UserID != null)
                {
                    var avail = protocol.UserID.Avaibility;
                    var account = protocol.AccountName;

                    Logging.Logger.AddInfo(string.Format("{0}: User has logged in with account {1} with state {2}.",
                        sender.ToString(), account, avail));
                }
                else
                    Logging.Logger.AddInfo(sender.ToString() + ": User has logged in, but UserID is unkown!");
            }
            if (OnLogin != null)
                OnLogin(this);
        }

    }
}
